package engine.services.impl;

import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID;

import javax.imageio.ImageIO;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import javax.mail.Address;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.mail.internet.MimeMessage.RecipientType;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.velocity.app.VelocityEngine;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
import org.springframework.security.util.InMemoryResource;
import org.springframework.ui.velocity.VelocityEngineUtils;

import engine.Consts;
import engine.content.ContentTypes;
import engine.content.FileContent;
import engine.content.HtmlContent;
import engine.content.IContent;
import engine.content.InMemoryContent;
import engine.dataobjects.MailItem;
import engine.parsers.IParser;
import engine.services.IContentService;
import engine.services.IMailService;
import engine.services.IXsltService;

public class MailService  implements IMailService {
	private static Log log = LogFactory.getLog(MailService.class);

	private boolean isDebugMode = false;
	
	private MailService() {
		
	}
	
	
	@Override
	public void setDebugMode() {
		isDebugMode = true;
	}
	
	/* (non-Javadoc)
	 * @see engine.beans.IMailService#mail(dataobjects.Message)
	 */
	public void mail(MailItem item) {
		try {
			MimeMessage mm = mailSender.createMimeMessage();
			if (isDebugMode) {
				item.getTo().clear();
				item.getTo().add(debugToAdress);
			}
			if (item.getTo() == null || item.getTo().size() == 0) {
				log.warn("This message has no recipients. Message: " + item.getName());
				return;
			}
			Address[] emailAddresses = new Address[item.getTo().size()];
			for (int i=0; i<item.getTo().size(); i++) {
				emailAddresses[i] = new InternetAddress(item.getTo().get(i));
			}
			mm.setRecipients(RecipientType.BCC, emailAddresses);
			mm.setSubject(item.getName());

			IContent messageContent = getContent(item);
			log.debug("Sending content of type: "+messageContent.getType() +" and content type " + messageContent.getContentType());
			if (messageContent.getType() == ContentTypes.FILE ||
					messageContent.getType() == ContentTypes.MEMORY) {
				MimeMessageHelper helper = new MimeMessageHelper(mm, true);
	            Resource res = null;
				if (messageContent.getType() == ContentTypes.FILE) {
					FileContent fc = (FileContent)messageContent;					
	            	res = new FileSystemResource(fc.getContent());	            			            		
				} else {
					InMemoryContent mc = (InMemoryContent)messageContent;
	            	res = new InMemoryResource(mc.getContent());	            			            			            		
				}
				Map<String, String> tmpltData = new HashMap<String, String>();
				tmpltData.put("replyTo", replyToAddress.getAddress());
				tmpltData.put("title", item.getName());				
	            String tmplt = null;
	            String fileId = UUID.randomUUID().toString();
	            // can only add this after the text. otherwise image won't show.
	            if (
	            		messageContent.getFileSuffix().equals("gif") || 
	            		messageContent.getFileSuffix().equals("jpeg") || 
	            		messageContent.getFileSuffix().equals("jpg") || 
	            		messageContent.getFileSuffix().equals("png")) 
				{
	            	// checking the size of our image
	            	BufferedImage img = ImageIO.read(res.getInputStream());
	            	int width = img.getWidth();
	            	if (width > Consts.MAX_INLINE_WIDTH) {
	            		float cropPercentage = Consts.MAX_INLINE_WIDTH/width;
	            		log.debug("cropping image by " + cropPercentage);
	            		String dimensionsStr = " width=\""+(int)Consts.MAX_INLINE_WIDTH+"\"";
	            		int height = img.getHeight();
	            		float croppedHeight = cropPercentage * height;
	            		dimensionsStr += " height=\""+ (int)croppedHeight+"\"";
	            		tmpltData.put("dimensions", dimensionsStr);
	            	} else {
		            	tmpltData.put("dimensions", "");
	            	}
	            	tmpltData.put("cid", fileId);
					tmplt = "emailText_Inline.tmplt";
				} else {
					String fileName = fileId + "." + messageContent.getFileSuffix();
					if (res instanceof InMemoryResource) {
		            	res = new InMemoryResource(((InMemoryContent)messageContent).getContent());	
						helper.addAttachment(fileName, res, messageContent.getContentType());
					} else {
						helper.addAttachment(fileName, ((FileContent)messageContent).getContent());						
					}
					tmplt = "emailText_File.tmplt";
				}
	            String text = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, "velocity/" + tmplt, tmpltData);
				helper.setText(text, true);
				if (res != null) {
					helper.addInline(fileId, res, fileId + "." + messageContent.getContentType());												
				}
			} else {
				String output = null;
				// checking for xslt transformation
				if (item.getXsltFile() != null && !item.getXsltFile().equals("")) {
					output = xsltService.transform(item.getXsltFile(), messageContent);
				} else {
					output = messageContent.getContentAsString();
				}
				mm.setContent(output, "text/html");
			}			

			if (messageContent.getContentBase() != null) {
                mm.setHeader("Content-Base", messageContent.getContentBase());
			}
			
			mm.setFrom(fromAdress);
			mm.setReplyTo(new InternetAddress[] {replyToAddress});
			mm.saveChanges();
			
			mailSender.send(mm);
		} catch (Exception e) {
			log.error("Problem sending mail." , e);
			throw new RuntimeException(e);
		}
	}
	

	private IContent getContent(MailItem item) throws Exception {
		IContent content = null;
		if (item.getUrl() == null) {
			content = new HtmlContent();
			content.setContent("");
			return content;
		}
		try {
			// getting the content for our message
			content = contentService.getResponse(item.getUrl());				
		} catch (Exception e) {
			log.error("No content found for: " + item.getUrl(), e);
			return null;
		}
		
		// checking for parser for content
		if (item.getParser() != null && !item.getParser().equals("")) {
			String parserClass = item.getParser();
			IParser parser = (IParser)Class.forName(parserClass).newInstance();
			content = parser.parse(content, contentService);
		}
						
		return content;
	}
	
	/****
	 * Injection Parameters
	 */
	private InternetAddress replyToAddress = null;
	private InternetAddress fromAdress = null;
	private String debugToAdress = null;
	
	public void setDebugToAdress(String debugToAdress){
		this.debugToAdress = debugToAdress;
	}

	@Autowired
	private JavaMailSenderImpl mailSender = null;
	
	public void setMailSender(JavaMailSenderImpl mailSender) {
		this.mailSender = mailSender;
	}

	public void setReplyToAddress(String replyToAddress) throws Exception {
		this.replyToAddress = new InternetAddress(replyToAddress);			
	}

	public void setFromAdress(String fromAdress) throws Exception{
		this.fromAdress = new InternetAddress(fromAdress);			
	}

	@Autowired
	IXsltService xsltService;
	
	@Autowired
	IContentService contentService;
	
	@Autowired
	VelocityEngine velocityEngine;
	
	public void setVelocityEngine(VelocityEngine velocityEngine) {
		this.velocityEngine = velocityEngine;
	}


	public void setContentService(IContentService contentService) {
		this.contentService = contentService;
	}


	public void setXsltService(IXsltService xsltService) {
		this.xsltService = xsltService;
	}
	

}
